package com.zhigongtech.core.web.aop;

import java.util.Arrays;
import java.util.Calendar;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.zhigongtech.common.web.util.IPUtils;
import com.zhigongtech.core.web.config.AsyncConfiguration;
import com.zhigongtech.core.web.jwt.UserUtils;
import com.zhigongtech.extension.entry.vo.LogVo;
import com.zhigongtech.extension.service.ILogFactoryService;

import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * 
 * @ClassName:  AopLog   
 * @Description: system log 
 * @author: Alan
 * @date:   2019-05-30 10:38:13
 *     
 * @Copyright: 2019 www.richfit.com Inc. All rights reserved.
 */
@Aspect
@Component
@Slf4j
@ConditionalOnBean({AsyncConfiguration.class, ILogFactoryService.class})
public class AopLog {
	
	@Autowired
	private ILogFactoryService logFactoryService;
	/**
     * 定义一个切入点.
     * 解释下：
     *
     * ~ 第一个 * 代表任意修饰符及任意返回值.
     * ~ 第二个 * 任意包名
     * ~ 第三个 * 代表任意方法.
     * ~ 第四个 * 定义在controller包或者子包
     * ~ 第五个 * 任意方法
     * ~ .. 匹配任意数量的参数.
     */
	//	切入点注解的表达式：就是需要AOP的地方(一般是业务逻辑层service,当然服务接口调用层controller也行,两者一起打印日志也行
    //	这个类似正则表达式,可以控制日志的精度(包下,类下,方法下)和切面的类型(业务层面,服务接口层面)相当灵活)
    @Pointcut("execution(* com.zhigongtech.tender.rest.*.controller..*.*(..))")
    public void mypointcut() {
    }
 
    //环绕增强
    @Around("mypointcut()")
    public Object aroundLogger(ProceedingJoinPoint jp) throws Throwable {
    	if(log.isDebugEnabled()) {
			log.debug("在==>>" + jp.getTarget().getClass().getName() + "类里面使用AOP环绕增强==");
	        log.debug("*环绕增强*调用【" + jp.getTarget().getClass().getSimpleName() + "】的【 " + jp.getSignature().getName() + "】方法。方法入参【" + Arrays.toString(jp.getArgs()) + "】");
    	}
		
		LogVo logVo = createVo(jp);
  
        try {
            Object result = jp.proceed();
            if(log.isDebugEnabled()) {
            	log.debug("*环绕增强*调用 " + jp.getTarget() + "的【 " + jp.getSignature().getName() + "】方法。方法返回值【" + JSONUtil.toJsonPrettyStr(result)  + "】");
            }
            logVo.setStatus(LogVo.STATUS_1);
            logVo.setResult(JSONUtil.toJsonStr(result));
            return result;
        } catch (Throwable e) {
            log.error(jp.getSignature().getName() + " 方法发生异常【" + e + "】");
            logVo.setStatus(LogVo.STATUS_0);
            logVo.setResult(JSONUtil.toJsonStr(e));
            throw e;
        } finally {
        	logFactoryService.createOperationLog(logVo);
        	if(log.isDebugEnabled()) {
        		log.debug("*环绕增强*执行finally【" + jp.getSignature().getName() + "】方法结束执行<<==。");
        	}
        }
    }
    
    /**
     * 
     * @Title: createVo   
     * @Description: 创建日志对象 缺少 status 和 result 属性
     * by Alan 2019-05-31 17:28:28
     * @param jp
     * @return  LogVo
     */
    private LogVo createVo(ProceedingJoinPoint jp) {
    	// 接收到请求，记录请求内容(这里同样可以在前置增强配置请求的相关信息)
		ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		HttpServletRequest request = attributes.getRequest();
		
    	LogVo vo = new LogVo();
    	vo.setClazzPath(jp.getTarget().getClass().getSimpleName());
    	vo.setMethodName(jp.getSignature().getName());
    	if(jp.getArgs() != null && jp.getArgs().length>0) {
    		vo.setParams(JSONUtil.toJsonPrettyStr(jp.getArgs()));
    	}
    	vo.setReqAccount(UserUtils.getCurrentUserLoginName());
    	vo.setReqUri(request.getRequestURI().toString().replace(request.getContextPath(), ""));
    	vo.setReqMethod(request.getMethod());
    	vo.setReqIp(IPUtils.getIpAddr(request));
    	vo.setReqLang(LocaleContextHolder.getLocale().toString());
    	vo.setClientBrowserLang(request.getHeader("Accept-Language"));
    	vo.setUserAgent(request.getHeader("User-Agent"));
    	vo.setCreateDate(Calendar.getInstance().getTime());
    	
    	if(log.isDebugEnabled()) {
	    	log.debug("请求的地址的用户 : " +vo.getReqAccount());
			log.debug("请求的地址URL : " + vo.getReqUri());
			log.debug("请求的方式HTTP_METHOD : " + vo.getReqMethod());
			log.debug("请求的IP : " + vo.getReqIp());
			log.debug("请求的全类名 : " + vo.getClazzPath() + "." + vo.getMethodName());
			log.debug("请求的参数(数组形式) : " + vo.getParams());
			log.debug("请求的语言 : " + vo.getReqLang());
			log.debug("请求的浏览器语言 : " + vo.getClientBrowserLang());
		}
    	
    	return vo;
    }
}
